﻿using SharedFizzBuzz;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;

namespace FizzBuzzTestProject
{
    
    
    /// <summary>
    ///This is a test class for FizzBuzzTest and is intended
    ///to contain all FizzBuzzTest Unit Tests
    ///</summary>
    [TestClass()]
    public class FizzBuzzTest
    {
        private FizzBuzz fizzBuzz;

        [TestInitialize]
        public void Setup() 
        {
            this.fizzBuzz = new FizzBuzz();    
        }

        [TestMethod]
        public void WeCanCreateANewFizzBuzz()
        {
            FizzBuzz target = new FizzBuzz();
        }

        [TestMethod]
        public void ATestOfANumberDivisibleByThreeShouldReturnFizz()
        {
            var target = new StandardFizzBuzzStrategy();
            string expected = "Fizz";
            string value = target.Transform(3);
            Assert.AreEqual(expected, value, String.Format("The value returned was expected to be: {0} but was really: {1}", expected, value));
        }

        [TestMethod]
        public void ATestOfANumberDivisibleByFiveShouldReturnBuzz()
        {
            var target = new StandardFizzBuzzStrategy();
            string expected = "Buzz";
            string value = target.Transform(5);
            Assert.AreEqual(expected, value, String.Format("The value returned was expected to be: {0} but was really: {1}", expected, value));
        }

        [TestMethod]
        public void ATestOfANumberDivisibleByThreeAndFiveShouldReturnFizzBuzz()
        {
            var target = new StandardFizzBuzzStrategy();
            string expected = "FizzBuzz";
            string value = target.Transform(15);
            Assert.AreEqual(expected, value, String.Format("The value returned was expected to be: {0} but was really: {1}", expected, value));
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldBeCreatableWithARange()
        {
            var target = new FizzBuzz(-50, 50);
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldBeCreatableWithASingleNumberInTheRange()
        {
            var target = new FizzBuzz(100, 100);
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldBeCreatableWithADecreasingRange()
        {
            var target = new FizzBuzz(100, 1);
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldBeCreatableWithDifferentTokensForFizzAndBuzz()
        {
            var target = new FizzBuzz("!Fizz", "!Buzz");
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldBeCreatableWithDifferentTokensForFizzAndBuzzAndARange()
        {
            var target = new FizzBuzz("!Fizz", "!Buzz", 100, 200);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void AlternatFizzTokensCannotBeNull()
        {
            var target = new FizzBuzz(null, "!Buzz", 100, 200);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void AlternatFizzTokensCannotBeEmpty()
        {
            var target = new FizzBuzz(string.Empty, "!Buzz", 100, 200);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void AlternatBuzzTokensCannotBeNull()
        {
            var target = new FizzBuzz("!Fizz", null, 100, 200);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void AlternatBuzzTokensCannotBeEmpty()
        {
            var target = new FizzBuzz("!Fizz", string.Empty, 100, 200);
        }

        [TestMethod]
        public void AlternateFizzAndBuzzTokensShouldBeSet()
        {
            var target = new GeneralFizzBuzzStrategy("!Fizz", "!Buzz");
            
            Assert.AreEqual(target.FizzToken, "!Fizz");
            Assert.AreEqual(target.BuzzToken, "!Buzz");
        }

        [TestMethod]
        public void TheDefaultConstructorShouldSetTheDefaultFizzAndBuzzTokens()
        {
            var target = new StandardFizzBuzzStrategy();

            Assert.AreEqual(target.FizzToken, "Fizz");
            Assert.AreEqual(target.BuzzToken, "Buzz");
        }

        [TestMethod]
        public void AlternateRangesShouldBeSet()
        {
            var target = new FizzBuzz("!Fizz", "!Buzz", 100, 200);

            Assert.AreEqual(target.StartRange, 100);
            Assert.AreEqual(target.EndRange, 200);
        }

        [TestMethod]
        public void TheDefaultConstructorShouldSetTheDefaultRange()
        {
            var target = new FizzBuzz();

            Assert.AreEqual(target.StartRange, 1);
            Assert.AreEqual(target.EndRange, 100);
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldCreateASeriesOfValuesToAnOutputStream()
        {
            var target = new FizzBuzz();
            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));

            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldWriteFizzToAnOutputStreamWhenItsRangeisOnlyTheValueThree()
        {
            var target = new FizzBuzz(3,3);
            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));
            Assert.AreEqual(writer.ToString().Trim(),"Fizz");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldWriteBuzzToAnOutputStreamWhenItsRangeisOnlyTheValueFive()
        {
            var target = new FizzBuzz(5, 5);
            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));
            Assert.AreEqual(writer.ToString().Trim(), "Buzz");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldWriteFizzBuzzToAnOutputStreamWhenItsRangeisOnlyTheValueFifteen()
        {
            var target = new FizzBuzz(15, 15);
            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));
            Assert.AreEqual(writer.ToString().Trim(), "FizzBuzz");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectWithAlternatTokensShouldWriteFizzTokenToAnOutputStreamWhenItsRangeisOnlyTheValueThree()
        {
            var target = new FizzBuzz("Foo", "Bar", 3, 3);
            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));
            Assert.AreEqual(writer.ToString().Trim(), "Foo");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectWithAlternatTokensShouldWriteBuzzTokenToAnOutputStreamWhenItsRangeisOnlyTheValueFive()
        {
            var target = new FizzBuzz("Foo", "Bar", 5, 5); 
            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));
            Assert.AreEqual(writer.ToString().Trim(), "Bar");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectWithAlternatTokensShouldWriteFizzAndBuzzTokenToAnOutputStreamWhenItsRangeisOnlyTheValueFifteen()
        {
            var target = new FizzBuzz("Foo", "Bar", 15, 15);
            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));
            Assert.AreEqual(writer.ToString().Trim(), "FooBar");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldPrintARangeOverZero()
        {
            var target = new FizzBuzz("Foo", "Bar", -1, 1);
            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));
            Assert.AreEqual(writer.ToString().Trim(), "-1\r\nFooBar\r\n1");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldPrintAReverseRange()
        {
            var target = new FizzBuzz("Foo", "Bar", 1, -1);
            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));
            Assert.AreEqual(writer.ToString().Trim(), "1\r\nFooBar\r\n-1");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldSupportManyTokensAndDivisors()
        {
            String[] tokens = new String[] { "Fizz", "Buzz", "Bazz" };
            int[] divisors = new int[] { 3, -5, 7 };

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);

            var target = new FizzBuzz(strategy, 1, 7);


            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));

            Assert.AreEqual(writer.ToString().Trim(), @"1
2
Fizz
4
Buzz
Fizz
Bazz");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldSupportSameTokensAndDivisorsManyTimes()
        {
            String[] tokens = new String[] { "Fizz", "Fizz", "Fizz" };
            int[] divisors = new int[] { 1, 1, 1 };

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);

            var target = new FizzBuzz(strategy, 1, 7);


            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));

            Assert.AreEqual(writer.ToString().Trim(), @"FizzFizzFizz
FizzFizzFizz
FizzFizzFizz
FizzFizzFizz
FizzFizzFizz
FizzFizzFizz
FizzFizzFizz");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        [ExpectedException (typeof(ArgumentNullException))]
        public void AVariableFizzBuzzStrategyObjectShouldThrowAnExpectionIfTokensAreNull()
        {
            String[] tokens = null;
            int[] divisors = new int[] { 1, 1, 1 };

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);
        }

        [TestMethod]
        [ExpectedException (typeof(ArgumentNullException))]
        public void AVariableFizzBuzzStrategyObjectShouldThrowAnExpectionIfDivisorsAreNull()
        {
            String[] tokens = new String[]{"foo"};
            int[] divisors = null;

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void AVariableFizzBuzzStrategyObjectShouldThrowAnExpectionIfADivisorIsZero()
        {
            String[] tokens = new String[] { "foo" };
            int[] divisors = new int[] { 0 };

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void AVariableFizzBuzzStrategyObjectShouldThrowAnExpectionIfTheInputArraysAreNotTheSameSize()
        {
            String[] tokens = new String[] { "foo" };
            int[] divisors = new int[] { 1 , 2};

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void AVariableFizzBuzzStrategyObjectShouldThrowAnExpectionIfTheTokenArrayContainsAnEmptyOrWhiteSpaceString()
        {
            String[] tokens = new String[] { "" };
            int[] divisors = new int[] { 1 };

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void AVariableFizzBuzzStrategyObjectShouldThrowAnExpectionIfTheTokenArrayContainsANull()
        {
            String[] tokens = new String[] { null };
            int[] divisors = new int[] { 1 };

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void AVariableFizzBuzzStrategyObjectShouldThrowAnExpectionIfTheTokenArrayIsEmpty()
        {
            String[] tokens = new String[] { };
            int[] divisors = new int[] { };

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldSupportXPlusOneNumberOfTokens()
        {
            String[] tokens = new String[] { "Fizz", "Buzz", "Bazz", "Foo", "Bar" };
            int[] divisors = new int[] { 1, 2, 3, 4, 5 };

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);

            var target = new FizzBuzz(strategy, 1, 5);


            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));

            Assert.AreEqual(writer.ToString().Trim(), @"Fizz
FizzBuzz
FizzBazz
FizzBuzzFoo
FizzBar");
            Console.Write(writer.ToString());
        }

        [TestMethod]
        public void AFizzBuzzObjectShouldSupportOneToken()
        {
            String[] tokens = new String[] { "Fizz" };
            int[] divisors = new int[] { 3 };

            var strategy = new VariableFizzBuzzStrategy(tokens, divisors);

            var target = new FizzBuzz(strategy, 1, 6);


            var writer = new StringWriter();
            target.Execute(writer);

            Assert.IsFalse(string.IsNullOrEmpty(writer.ToString()));

            Assert.AreEqual(writer.ToString().Trim(), @"1
2
Fizz
4
5
Fizz");
            Console.Write(writer.ToString());
        }
    }
}
